// utils/ip/enter.go
// =============================================================================
// | 包: ip                                                                   |
// | 功能: 提供IP地址相关的工具函数，包括内网IP检测、客户端真实IP获取等功能    |
// =============================================================================
package ip

import (
	"net"       // 网络包 - 提供网络相关操作
	"net/http"  // HTTP包 - 提供HTTP客户端和服务器实现
	"strings"   // 字符串包 - 提供字符串操作函数
)

// =============================================================================
// | 函数: HasLocalIPAddr                                                     |
// | 参数: @ip - string - 要检测的IP地址字符串                                |
// | 返回: @bool - 如果是内网地址则返回true，否则返回false                    |
// | 功能: 检测IP地址字符串是否是内网地址                                     |
// | 说明: 调用HasLocalIP函数进行实际检测，提供字符串版本的IP内网检测功能      |
// =============================================================================
func HasLocalIPAddr(ip string) bool {
	return HasLocalIP(net.ParseIP(ip)) // 解析字符串IP并调用HasLocalIP函数检测
}

// =============================================================================
// | 函数: HasLocalIP                                                         |
// | 参数: @ip - net.IP - 要检测的IP地址对象                                  |
// | 返回: @bool - 如果是内网地址则返回true，否则返回false                    |
// | 功能: 检测IP地址是否是内网地址                                           |
// | 说明: 通过直接对比IP段范围进行检测，效率更高                              |
// |----------------------------------------------------------------------------|
// | 流程:                                                                     |
// | 1. 检查是否是回环地址                                                     |
// | 2. 转换为IPv4地址并检查                                                   |
// | 3. 按顺序检查是否属于各内网IP范围                                        |
// =============================================================================
func HasLocalIP(ip net.IP) bool {
	if ip.IsLoopback() {               // 检查是否是回环地址（127.0.0.0/8）
		return true
	}

	ip4 := ip.To4()                    // 转换为IPv4地址
	if ip4 == nil {                    // 如果不是IPv4地址，则不是内网地址
		return false
	}

	return ip4[0] == 10 || // 10.0.0.0/8
		(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
		(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
		(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
}

// =============================================================================
// | 函数: ClientPublicIP                                                     |
// | 参数: @r - *http.Request - HTTP请求对象                                  |
// | 返回: @string - 客户端的公网IP地址字符串，如果无法获取则返回空字符串      |
// | 功能: 尽最大努力实现获取客户端公网IP的算法                                |
// | 说明: 解析X-Real-IP和X-Forwarded-For以便于反向代理（nginx或haproxy）可以正常工作 |
// |----------------------------------------------------------------------------|
// | 流程:                                                                     |
// | 1. 从X-Forwarded-For头中提取第一个非内网IP                               |
// | 2. 从X-Real-Ip头中提取非内网IP                                          |
// | 3. 从RemoteAddr中提取非内网IP                                            |
// | 4. 都无法获取时返回空字符串                                              |
// =============================================================================
func ClientPublicIP(r *http.Request) string {
	var ip string
	for _, ip = range strings.Split(r.Header.Get("X-Forwarded-For"), ",") { // 遍历X-Forwarded-For中的所有IP
		if ip = strings.TrimSpace(ip); ip != "" && !HasLocalIPAddr(ip) {   // 去除空格并检查是否是非内网IP
			return ip
		}
	}

	if ip = strings.TrimSpace(r.Header.Get("X-Real-Ip")); ip != "" && !HasLocalIPAddr(ip) { // 检查X-Real-Ip头
		return ip
	}

	if ip = RemoteIP(r); !HasLocalIPAddr(ip) { // 检查RemoteAddr中的IP
		return ip
	}

	return "" // 都无法获取时返回空字符串
}

// =============================================================================
// | 函数: RemoteIP                                                           |
// | 参数: @r - *http.Request - HTTP请求对象                                  |
// | 返回: @string - 从RemoteAddr中提取的IP地址字符串                          |
// | 功能: 通过RemoteAddr获取IP地址，只是一个快速解析方法                      |
// | 说明: 直接从请求的RemoteAddr字段中提取IP地址，不做任何内网检测           |
// =============================================================================
func RemoteIP(r *http.Request) string {
	ip, _, _ := net.SplitHostPort(r.RemoteAddr) // 从RemoteAddr中分离出IP和端口
	return ip                                   // 返回IP部分
}
